/**
 * NOTE:
 *
 * HAL_TCP_xxx API reference implementation: wrappers/os/ubuntu/HAL_TCP_linux.c
 *
 */
#include "infra_types.h"
#include "infra_defs.h"
#include "wrappers_defs.h"
#include "stdarg.h"

#include "nbiot.h"
#include "main.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


#define PRODUCT_KEY      "a1XhgGKWdyh"
#define DEVICE_NAME      "BearKE1_Test"
#define DEVICE_SECRET    "ZkAhUytWsWbtW6QEoOr6UoezTRyJdBsh"


char g_acRbuf[256];
char g_acRtmp[256];


/**
 * @brief Deallocate memory block
 *
 * @param[in] ptr @n Pointer to a memory block previously allocated with platform_malloc.
 * @return None.
 * @see None.
 * @note None.
 */
void HAL_Free(void *ptr)
{
	free(ptr);
}


/**
 * @brief Get device name from user's system persistent storage
 *
 * @param [ou] device_name: array to store device name, max length is IOTX_DEVICE_NAME_LEN
 * @return the actual length of device name
 */
int HAL_GetDeviceName(char device_name[IOTX_DEVICE_NAME_LEN + 1])
{
	strcpy(device_name, DEVICE_NAME);	
	return (int)strlen(DEVICE_NAME);
}


/**
 * @brief Get device secret from user's system persistent storage
 *
 * @param [ou] device_secret: array to store device secret, max length is IOTX_DEVICE_SECRET_LEN
 * @return the actual length of device secret
 */
int HAL_GetDeviceSecret(char device_secret[IOTX_DEVICE_SECRET_LEN + 1])
{
	strcpy(device_secret, DEVICE_SECRET);	
	return (int)strlen(DEVICE_SECRET);
}


/**
 * @brief Get firmware version
 *
 * @param [ou] version: array to store firmware version, max length is IOTX_FIRMWARE_VER_LEN
 * @return the actual length of firmware version
 */
int HAL_GetFirmwareVersion(char *version)
{
	return (int)1;
}


/**
 * @brief Get product key from user's system persistent storage
 *
 * @param [ou] product_key: array to store product key, max length is IOTX_PRODUCT_KEY_LEN
 * @return  the actual length of product key
 */
int HAL_GetProductKey(char product_key[IOTX_PRODUCT_KEY_LEN + 1])
{
	strcpy(product_key, PRODUCT_KEY);	
	return (int)strlen(PRODUCT_KEY);
}


int HAL_GetProductSecret(char product_secret[IOTX_PRODUCT_SECRET_LEN + 1])
{
	return (int)1;
}


/**
 * @brief Allocates a block of size bytes of memory, returning a pointer to the beginning of the block.
 *
 * @param [in] size @n specify block size in bytes.
 * @return A pointer to the beginning of the block.
 * @see None.
 * @note Block value is indeterminate.
 */
void *HAL_Malloc(uint32_t size)
{
	return malloc(size);
}


/**
 * @brief Create a mutex.
 *
 * @retval NULL : Initialize mutex failed.
 * @retval NOT_NULL : The mutex handle.
 * @see None.
 * @note None.
 */
void *HAL_MutexCreate(void)
{
	return (void*)1;
}


/**
 * @brief Destroy the specified mutex object, it will release related resource.
 *
 * @param [in] mutex @n The specified mutex.
 * @return None.
 * @see None.
 * @note None.
 */
void HAL_MutexDestroy(void *mutex)
{
	return;
}


/**
 * @brief Waits until the specified mutex is in the signaled state.
 *
 * @param [in] mutex @n the specified mutex.
 * @return None.
 * @see None.
 * @note None.
 */
void HAL_MutexLock(void *mutex)
{
	return;
}


/**
 * @brief Releases ownership of the specified mutex object..
 *
 * @param [in] mutex @n the specified mutex.
 * @return None.
 * @see None.
 * @note None.
 */
void HAL_MutexUnlock(void *mutex)
{
	return;
}


/**
 * @brief Writes formatted data to stream.
 *
 * @param [in] fmt: @n String that contains the text to be written, it can optionally contain embedded format specifiers
     that specifies how subsequent arguments are converted for output.
 * @param [in] ...: @n the variable argument list, for formatted and inserted in the resulting string replacing their respective specifiers.
 * @return None.
 * @see None.
 * @note None.
 */
void HAL_Printf(const char *fmt, ...)
{
    va_list args;   
	va_start(args, fmt);    
	vprintf(fmt, args);    
	va_end(args);    
	fflush(stdout);
}


uint32_t HAL_Random(uint32_t region)
{
	return (region > 0) ? (rand() % region) : 0;
}


/**
 * @brief Sleep thread itself.
 *
 * @param [in] ms @n the time interval for which execution is to be suspended, in milliseconds.
 * @return None.
 * @see None.
 * @note None.
 */
void HAL_SleepMs(uint32_t ms)
{
	HAL_Delay(ms);
}


/**
 * @brief Writes formatted data to string.
 *
 * @param [out] str: @n String that holds written text.
 * @param [in] len: @n Maximum length of character will be written
 * @param [in] fmt: @n Format that contains the text to be written, it can optionally contain embedded format specifiers
     that specifies how subsequent arguments are converted for output.
 * @param [in] ...: @n the variable argument list, for formatted and inserted in the resulting string replacing their respective specifiers.
 * @return bytes of character successfully written into string.
 * @see None.
 * @note None.
 */
int HAL_Snprintf(char *str, const int len, const char *fmt, ...)
{
    va_list args;    
	int rc;    

	va_start(args, fmt);   
	rc = vsnprintf(str, len, fmt, args);   
	va_end(args); 
	
	return rc;
}


void HAL_Srandom(uint32_t seed)
{
	srand(seed);
}


/**
 * @brief Destroy the specific TCP connection.
 *
 * @param [in] fd: @n Specify the TCP connection by handle.
 *
 * @return The result of destroy TCP connection.
 * @retval < 0 : Fail.
 * @retval   0 : Success.
 */
    int HAL_TCP_Destroy(uintptr_t fd)
{
	BC28_TCP_CTX tcp_ctx;

	nbiot_tcp_ctx_init(&tcp_ctx);

	if(fd > 0)
		tcp_ctx.tcp_close(fd);

	return (int)0;
}


/**
 * @brief Establish a TCP connection.
 *
 * @param [in] host: @n Specify the hostname(IP) of the TCP server
 * @param [in] port: @n Specify the TCP port of TCP server
 *
 * @return The handle of TCP connection.
   @retval   0 : Fail.
   @retval > 0 : Success, the value is handle of this TCP connection.
 */
    uintptr_t HAL_TCP_Establish(const char *host, uint16_t port)
{
	int iFd = -1;
	char ip[16];
	BC28_TCP_CTX tcp_ctx;
	int iRet = 0;

	memset(ip, 0, 16);	
	//strcpy(ip, "116.211.167.65");

	nbiot_tcp_ctx_init(&tcp_ctx);

	iFd = tcp_ctx.tcp_create(0);	
	if(iFd <= 0 )	
		return 0;

	if(0 == ip[0]){
		iRet = tcp_ctx.net_ctx.iDomainNameResolve((char *)host, ip);
		if(iRet != 0){
			//HAL_TCP_Destroy(iFd);
			//return 0;
			strcpy(ip, "116.211.167.65");
		}
	}

	if( 0 != tcp_ctx.tcp_conn(iFd, ip, port)){
		HAL_TCP_Destroy(iFd);
		return 0;
	}

	return (uintptr_t)iFd;
}

int bHexToByte (char *sz_Hex, unsigned char *pbt_Byte, int *pi_ByteLength) 
{
	int iLength = strlen(sz_Hex);
	int iPos = 0, iLeft, iRight, iIndex;

	if (!sz_Hex) return 0;

	for (iIndex = 0; iIndex < iLength; iIndex++, iPos++) {
		iLeft = sz_Hex[iIndex++];
		iRight = sz_Hex[iIndex];

		iLeft = (iLeft < 58) ? (iLeft - 48) : (iLeft - 55);
		iRight = (iRight < 58) ? (iRight - 48) : (iRight - 55);

        pbt_Byte[iPos] = (iLeft << 4) | iRight;
	}

	*pi_ByteLength = iPos;

    return 1;
}

/**
 * @brief Read data from the specific TCP connection with timeout parameter.
 *        The API will return immediately if 'len' be received from the specific TCP connection.
 *
 * @param [in] fd @n A descriptor identifying a TCP connection.
 * @param [out] buf @n A pointer to a buffer to receive incoming data.
 * @param [out] len @n The length, in bytes, of the data pointed to by the 'buf' parameter.
 * @param [in] timeout_ms @n Specify the timeout value in millisecond. In other words, the API block 'timeout_ms' millisecond maximumly.
 *
 * @retval       -2 : TCP connection error occur.
 * @retval       -1 : TCP connection be closed by remote server.
 * @retval        0 : No any data be received in 'timeout_ms' timeout period.
 * @retval (0, len] : The total number of bytes be received in 'timeout_ms' timeout period.

 * @see None.
 */
    int32_t HAL_TCP_Read(uintptr_t fd, char *buf, uint32_t len, uint32_t timeout_ms)
{
	static int iLen = 0;  //string len
	static int iRlen = 0;
	int i = 0;
	int iRet = 0;
	BC28_TCP_CTX tcp_ctx;
	
	nbiot_tcp_ctx_init(&tcp_ctx);


	if(iLen == 0){
		iRet = tcp_ctx.tcp_wait(&iLen, timeout_ms/50);
		if( iRet == 4 ) return -1;
		if( iLen == 0 ) return 0;
	
		memset(g_acRbuf, 0, sizeof(g_acRbuf));

		tcp_ctx.tcp_recv(fd, &iLen, (unsigned char *)g_acRbuf, sizeof(g_acRbuf), timeout_ms);

		iRlen = 0;
		
	}

	if(iLen >= len * 2){
		iLen -= len * 2;
	}else{
		iLen = 0;
	}

	memset(g_acRtmp, 0, sizeof(g_acRtmp));
	memcpy(g_acRtmp, g_acRbuf + iRlen, len*2);
	iRlen += len*2;

	bHexToByte(g_acRtmp, (unsigned char *)buf, (int *)&len);

	printf("Return:[%d]", len);
	for(i = 0; i < len; i++){
		printf("%02x", buf[i]);
	}
	printf("\n");
	
	return (int32_t)len;
}


/**
 * @brief Write data into the specific TCP connection.
 *        The API will return immediately if 'len' be written into the specific TCP connection.
 *
 * @param [in] fd @n A descriptor identifying a connection.
 * @param [in] buf @n A pointer to a buffer containing the data to be transmitted.
 * @param [in] len @n The length, in bytes, of the data pointed to by the 'buf' parameter.
 * @param [in] timeout_ms @n Specify the timeout value in millisecond. In other words, the API block 'timeout_ms' millisecond maximumly.
 *
 * @retval      < 0 : TCP connection error occur..
 * @retval        0 : No any data be write into the TCP connection in 'timeout_ms' timeout period.
 * @retval (0, len] : The total number of bytes be written in 'timeout_ms' timeout period.

 * @see None.
 */
    int32_t HAL_TCP_Write(uintptr_t fd, const char *buf, uint32_t len, uint32_t timeout_ms)
{
	int iLoop = 0;
	int iLast = 0;
	int iSent = 0;
	int iBlock = 512;
	int i = 0;
	BC28_TCP_CTX tcp_ctx;
	
	nbiot_tcp_ctx_init(&tcp_ctx);

	//if( tcp_ctx.net_ctx.iNetworkCheck() != 0 )		
	//	return -1;
	
	iLoop = len / iBlock;
	iLast = len % iBlock;
	
	for(i = 0; i < iLoop; i++){
		tcp_ctx.tcp_send(fd, (unsigned char *)buf+iSent, (int)iBlock, timeout_ms/100);
		iSent += iBlock;
	}
	
	if(iLast > 0){
		tcp_ctx.tcp_send(fd, (unsigned char *)buf+(len-iLast), (int)iLast, timeout_ms/100);
	}
	
	return (int32_t)len;
}


/**
 * @brief Retrieves the number of milliseconds that have elapsed since the system was boot.
 *
 * @return the number of milliseconds.
 * @see None.
 * @note None.
 */
uint64_t HAL_UptimeMs(void)
{
	return (uint64_t)HAL_GetTick();
}


int HAL_Vsnprintf(char *str, const int len, const char *format, va_list ap)
{
	return vsnprintf(str, len, format, ap);
}


